#pragma once

#include <typeinfo>
#include <vector>
#include <sstream>
#include "serial.h"

NAMESPACE_BEGIN(kog);
class remote_function;

NAMESPACE_BEGIN(tr);

template<typename _Fun> struct function_wrap_t;

template<typename _Class, typename _ReturnType>
struct function_wrap_t<_ReturnType (_Class::*)()>
{
	typedef _ReturnType (_Class::*memfun)();
	typedef _ReturnType (*rawfun)();

	function_wrap_t(memfun fun, _Class* obj)
		: _obj(obj), _fun(fun)
	{}

	_ReturnType operator()()
	{
		return (_obj->*_fun)();
	}

	_Class* _obj;
	memfun _fun;
};

template<typename _Class, typename _ReturnType, typename _Argument>
struct function_wrap_t<_ReturnType (_Class::*)(_Argument)>
{
	typedef _ReturnType (_Class::*memfun)(_Argument);
	typedef _ReturnType (*rawfun)(_Argument);

	function_wrap_t(memfun fun, _Class* obj)
		: _obj(obj), _fun(fun)
	{}

	_ReturnType operator()(_Argument arg)
	{
		return (_obj->*_fun)(arg);
	}

	_Class* _obj;
	memfun _fun;
};

template<typename _Class, typename _ReturnType, typename _Argument1, typename _Argument2>
struct function_wrap_t<_ReturnType (_Class::*)(_Argument1, _Argument2)>
{
	typedef _ReturnType (_Class::*memfun)(_Argument1, _Argument2);
	typedef _ReturnType (*rawfun)(_Argument1, _Argument2);

	function_wrap_t(memfun fun, _Class* obj)
		: _obj(obj), _fun(fun)
	{}

	_ReturnType operator()(_Argument1 arg1, _Argument2 arg2)
	{
		return (_obj->*_fun)(arg1, arg2);
	}

	_Class* _obj;
	memfun _fun;
};

template <typename _Fun, typename _Class> function_wrap_t<_Fun> function_wrap(_Fun fun, _Class* obj)
{
	function_wrap_t<_Fun> tmp(fun, obj);
	return tmp;
}


struct function_proxy
{
	friend class remote_function;
protected:
	function_proxy(remote_function& rf, const std::string& fname)
		: _rf(&rf)
		, _name(fname)
		, _cid(0)
	{}
public:
	template<typename _ReturnType> function_proxy& makecall_async()
	{
		// serial call meta
		typedef _ReturnType (*function_type)();
		_serial_header<function_type>();

		std::string buf;
		_sr.getbuffer(buf);
		_cid = _rf->_send_fcall(buf.c_str(), buf.size());
		if (_cid == 0) throw std::runtime_error("make new call failed!");

		return *this;
	}

	template<typename _ReturnType> _ReturnType makecall()
	{
		_ReturnType v;
		makecall_async<_ReturnType>();
		if (0 == get_result(v)) return v;
		
		throw std::runtime_error("call failed!");
	}

	template<typename _ReturnType, typename _Argument> function_proxy& makecall_async(_Argument arg)
	{
		// serial call meta
		typedef _ReturnType (*function_type)(_Argument);
		_serial_header<function_type>();
		_sr.append(arg);

		std::string buf;
		_sr.getbuffer(buf);

		_cid = _rf->_send_fcall(buf.c_str(), buf.size());
		if (_cid == 0) throw std::runtime_error("make new call failed!");

		return *this;
	}

	template<typename _ReturnType, typename _Argument> _ReturnType makecall(_Argument arg)
	{
		_ReturnType v;
		makecall_async<_ReturnType>(arg);
		if (0 == get_result(v)) return v;
		
		throw std::runtime_error("call failed!");
	}

	template<typename _ReturnType, typename _Argument1, typename _Argument2> 
	function_proxy& makecall_async(_Argument1 arg1, _Argument2 arg2)
	{
		// serial call meta
		typedef _ReturnType (*function_type)(_Argument1, _Argument2);
		_serial_header<function_type>();
		_sr.append(arg1);
		_sr.append(arg2);

		std::string buf;
		_sr.getbuffer(buf);

		_cid = _rf->_send_fcall(buf.c_str(), buf.size());
		if (_cid == 0) throw std::runtime_error("make new call failed!");

		return *this;
	}

	template<typename _ReturnType, typename _Argument1, typename _Argument2> 
	_ReturnType makecall(_Argument1 arg1, _Argument2 arg2)
	{
		_ReturnType v;
		makecall_async<_ReturnType>(arg1, arg2);
		if (0 == get_result(v)) return v;
		
		throw std::runtime_error("call failed!");
	}
public:
	// start 
	int start_service();

	int stop_service();
public:
	int waitsend();
	int waitresponse();
	template<typename _ReturnType> int waitresult(_ReturnType& v)
	{
		return _rf->_wait_result<_ReturnType>(_cid, v);
	}
private:
	template<typename _Fun> void _serial_header()
	{
		_sr.append(_name).append(std::string(typeid(_Fun).name()));
	}

	template<typename _ReturnType> int get_result(_ReturnType& v)
	{
		if (waitsend() != 0) throw std::runtime_error("wait send failed!");
		if (waitresponse() != 0) throw std::runtime_error("wait response failed!");
		if (waitresult<_ReturnType>(v) != 0) throw std::runtime_error("wait result failed!");

		return 0;
	}
private:
	kog::remote_function* _rf;
	std::string _name;
	serial _sr;
	int _cid; // call id
};

struct ifunction_t
{
	virtual void operator()(unserial& us, serial& s) = 0;
};

template <typename _RawType, typename _FunType> struct function_t;

template <typename _ReturnType, typename _FunType>
struct function_t<_ReturnType (*)(), _FunType> : public ifunction_t
{
public:
	function_t(_FunType fun)
		: _fun(fun)
	{
	}

	/* overwrite */ virtual void operator()(unserial& us, serial& s)
	{
		s.append(_fun());
	}
private:
	_FunType _fun;
};

template <typename _ReturnType, typename _Argument, typename _FunType>
struct function_t<_ReturnType (*)(_Argument), _FunType> : public ifunction_t
{
public:
	function_t(_FunType fun)
		: _fun(fun)
	{
	}

	/* overwrite */ virtual void operator()(unserial& us, serial& s)
	{
		_Argument arg;
		us.getvalue(arg);
		s.append(_fun(arg));
	}
private:
	_FunType _fun;
};

template <typename _ReturnType, typename _Argument1, typename _Argument2, typename _FunType>
struct function_t<_ReturnType (*)(_Argument1, _Argument2), _FunType> : public ifunction_t
{
public:
	function_t(_FunType fun)
		: _fun(fun)
	{
	}

	/* overwrite */ virtual void operator()(unserial& us, serial& s)
	{
		_Argument1 arg1;
		_Argument2 arg2;
		us.getvalue(arg1);
		us.getvalue(arg2);
		s.append(_fun(arg1, arg2));
	}
private:
	_FunType _fun;
};

NAMESPACE_END(tr);
NAMESPACE_END(kog);